#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include "shmem.h"
#include "cJSON.h"
#include "list.h"
#include "msg_queue_peer.h"
#include <pthread.h>
#include <sys/time.h> //struct timeval结构体的头文件

// 共用体
union val_t
{
    int b_val;   // bool类型存储空间
    int i_val;   // 整形值存储空间
    float f_val; // 浮点值存储空间
};

// 共享内存结构体
struct std_node
{
    int key;             // 唯一键值
    int type;            // 数据点类型
    int dev_type;        // 数据点属于哪个设备，根据网关支持的设备自行定义
    union val_t old_val; // 变化上报后需要更新旧值
    union val_t new_val; // 从共享内存取出最新数据，放到new_val中
    int ret;             // 默认为-1，采集成功后设置为0，采集失败再置-1
};

// 消息队列结构体
typedef struct control
{
    long dev_type;
    int key;
    union val_t val;
} con_t, *con_p;

// 消息队列
con_t msg_rev;
char sendbuf[256] = {0};
// 消息队列线程函数
void *handler(void *arg)
{
    intptr_t acc = (intptr_t)arg;//intptr_t 的大小与指针（void*）的大小严格一致,32位系统中intptr_t是32位有符号整数,64位系统中，intptr_t是64位有符号整数
    while (1)
    {
        // 发送前检查acc是否有效
        if (acc < 0)
        {
            printf("TCP连接已失效,重新等待连接...\n");
            break; // 退出线程，主线程会重新接受连接
        }
        else
        {
            printf("TCP已连接，acc=%ld,准备接收指令\n", acc);
        }

        // 消息队列接收

        if (msg_queue_recv("control", &msg_rev, sizeof(msg_rev), 1, 0) < 0)
        {
            printf("msg recv err\n");
        }
        else
        {
            printf("msg_receive:%d %d\n", msg_rev.key, msg_rev.val.i_val);
            sprintf(sendbuf, "Device_ID:1 %d %d", msg_rev.key, msg_rev.val.i_val);
        }
        // 7.发送数据
        int sen = send(acc, sendbuf, sizeof(sendbuf), 0);
        if (sen < 0)
        {
            perror("send err");
            continue;
            // return -1;
        }
    }
}
int main(int argc, char const *argv[])
{
    // 共享内存
    struct shm_param para;
    char *name = "shm";
    size_t size = 1024;
    // 初始化
    if (shm_init(&para, name, size) < 0)
    {
        printf("shm init err\n");
        return -1;
    }
    // 获取共享内存地址
    int *ptr = (int *)shm_getaddr(&para);
    if (ptr == NULL)
    {
        printf("shm get addr err\n");
        return -1;
    }

    // TCP链接
    // 1.创建套接字
    int soc = socket(AF_INET, SOCK_STREAM, 0);
    if (soc < 0)
    {
        perror("socket err");
        return -1;
    }
    // 2.填充结构体
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8880);
    saddr.sin_addr.s_addr = inet_addr("192.168.51.232"); // 虚拟机ip
    socklen_t len = sizeof(caddr);
    // 3.绑定信息
    int ret = bind(soc, (struct sockaddr *)&saddr, sizeof(saddr));
    if (ret < 0)
    {
        perror("bind err");
        return -1;
    }
    // 4.监听
    int lis = listen(soc, 5);
    if (lis < 0)
    {
        perror("listen err");
        return -1;
    }
    // 5.阻塞等待链接客户端
    while (1)
    {
        int acc = accept(soc, (struct sockaddr *)&caddr, &len);
        if (acc < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("accept ok\n");
        printf("client ip:%s port:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

        // 设置接收超时（避免网络断开时recv永久阻塞）
        struct timeval tv;
        tv.tv_sec = 10;                                                    // 10秒无数据则超时
        tv.tv_usec = 0;                                                    // 微秒数：0微秒
        if (setsockopt(acc, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) // SOL_SOCKET 表示通用套接字选项（适用于所有协议）,SO_RCVTIMEO 表示设置​​接收超时时间​​
        {
            perror("setsockopt SO_RCVTIMEO failed");
            close(acc); // 设置失败，关闭连接
            continue;   // 跳过当前客户端，继续等待新连接
        }
        // 创建线程
        pthread_t tid;
        if (pthread_create(&tid, NULL, handler, (void *)(intptr_t)acc) != 0)
        {
            perror("create err");
            return -1;
        }

        // 6.接收单片机数据  写共享内存
        while (1)
        {
            char buf[1024] = {0};
            int rec = recv(acc, buf, sizeof(buf), 0);
            if (rec < 0)
            {
                perror("receive err");
                close(acc); // 关闭失效连接
                break;      // 退出循环，重新接受连接
            }
            else if (rec == 0)
            {
                printf("客户端退出\n");
                close(acc); // 关闭链接
                break;      // 退出循环，重新接受连接
            }
            else
            {
                // printf("%s\n", a);
                // 反序列化
                cJSON *root = cJSON_Parse(buf);
                // 空指针检查，避免访问非法内存
                if (root == NULL)
                {
                    const char *error = cJSON_GetErrorPtr(); // 获取解析失败的错误信息
                    printf("JSON解析失败：%s\n", error);     // 打印错误详情（如语法错误、格式非法）
                    memset(buf, 0, sizeof(buf));             // 清空接收缓冲区，避免脏数据影响下次解析
                    continue;                                // 跳过当前无效数据，处理下一个接收到的数据
                }

                // 获取数组节点
                cJSON *data = cJSON_GetObjectItem(root, "data");
          
                // 获取数组大小
                int arrsize = cJSON_GetArraySize(data);

                //定义遍历指针st遍历共享内存，找到单片机区域的第一个位置
                struct std_node *st = (struct std_node *)(ptr + 1);
                for (int i = 0; i < *ptr; i++)
                {
                    if (st->key == 301)
                    {
                        //定义赋值指针p在相应位置把数据写入共享内存
                        struct std_node *p = st;
                        for (int j = 0; j < arrsize; j++)
                        {
                            // 获取数组下标为i的object节点
                            cJSON *arr = cJSON_GetArrayItem(data, j);
                            cJSON *item = cJSON_GetObjectItem(arr, "type");
                            switch (item->valueint)
                            {
                            case 1:
                                item = cJSON_GetObjectItem(arr, "val");
                                p->new_val.b_val = item->valueint;
                                break;
                            case 2:
                                item = cJSON_GetObjectItem(arr, "val");
                                p->new_val.i_val = item->valueint;
                                break;
                            case 3:
                                item = cJSON_GetObjectItem(arr, "val");
                                p->new_val.f_val = item->valuedouble;
                                break;
                            }
                            p++;
                        }
                    }
                    st++;
                }
                cJSON_Delete(root);
            }
            memset(buf, 0, sizeof(buf));
        }
        close(acc);
    }
    // 8.关闭套接字
    close(soc);

    return 0;
}
